package helperx

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"errors"
)

// AesEncrypt 加密
// key 必须 16 24 32位
// iv 必须16位
// 一般是将32位密钥的前16位传给key，后16位传给iv
// raw 为 false 则会输出base64
func AesEncrypt(plain string, key string, iv string, raw bool) (string, error) {
	origData := []byte(plain)
	block, err := aes.NewCipher([]byte(key))
	if err != nil {
		return "", err
	}

	if plain == "" {
		return "", errors.New("empty data")
	}
	if !validKey(key) {
		return "", errors.New("invalid key size")
	}
	if !validIv(iv) {
		return "", errors.New("invalid iv size")
	}

	// 获取秘钥块的长度
	blockSize := block.BlockSize()
	// 补全码
	origData = PKCS7Padding(origData, blockSize)
	// 加密模式
	blockMode := cipher.NewCBCEncrypter(block, []byte(iv))
	// 创建数组
	crypt := make([]byte, len(origData))
	// 加密
	blockMode.CryptBlocks(crypt, origData)

	if raw {
		return string(crypt), nil
	} else {
		return base64.StdEncoding.EncodeToString(crypt), nil
	}
}

// AesDecrypt 解密
// key 必须 16 24 32位
// iv 必须16位
// 一般是将32位密钥的前16位传给key，后16位传给iv
// raw 为 false 则会将secret 进行base64 decode
func AesDecrypt(secret string, key string, iv string, raw bool) (string, error) {
	cryptByte := []byte(secret)
	if true != raw {
		cryptByte, _ = base64.StdEncoding.DecodeString(secret)
	}

	// 检查
	if secret == "" {
		return "", errors.New("empty secret")
	}
	if !validKey(key) {
		return "", errors.New("invalid key size")
	}
	if !validIv(iv) {
		return "", errors.New("invalid iv size")
	}

	// 分组秘钥
	block, err := aes.NewCipher([]byte(key))
	if err != nil {
		return "", err
	}
	// 密文长度
	blockSize := block.BlockSize()
	if len(cryptByte)%blockSize != 0 {
		return "", errors.New("invalid secret block size")
	}

	// 加密模式
	blockMode := cipher.NewCBCDecrypter(block, []byte(iv))
	// 创建数组
	orig := make([]byte, len(cryptByte))
	// 解密
	blockMode.CryptBlocks(orig, cryptByte)
	// 去补全码
	orig = PKCS7UnPadding(orig)

	return string(orig), nil
}

// PKCS7Padding 补码 AES加密数据块分组长度必须为128bit(byte[16])，密钥长度可以是128bit(byte[16])、192bit(byte[24])、256bit(byte[32])中的任意一个。
func PKCS7Padding(cipherText []byte, blockSize int) []byte {
	padding := blockSize - len(cipherText)%blockSize
	padText := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(cipherText, padText...)
}

// PKCS7UnPadding 去码
func PKCS7UnPadding(origData []byte) []byte {
	length := len(origData)
	unPadding := int(origData[length-1])
	if unPadding >= length {
		return nil
	}
	return origData[:(length - unPadding)]
}

// 合法的key
func validKey(key string) bool {
	if IntInArray(len(key), []int{16, 24, 32}) {
		return true
	}

	return false
}

// 合法的iv
func validIv(iv string) bool {
	return len(iv) == 16
}
